﻿/////////////////////////////////////////////////////////////////
//
// Silverlight 5: 3D Physics using Balder and Jiglibx
// by Andy Beaulieu - http://www.andybeaulieu.com
//
// Credit for this camera class goes to Sgt Conker's Xna Camera tutorial:
//    http://www.sgtconker.com/2010/09/article-simple-3d-camera-in-xna/
//
//
// LICENSE: This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ANY 
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS
// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
/////////////////////////////////////////////////////////////////

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using xna=Microsoft.Xna.Framework;

namespace Spritehand.Physics3D
{
    public class Camera
    {
        public enum CameraMode
        {
            free = 0,
            chase = 1,
            orbit = 2
        }
        public CameraMode currentCameraMode = CameraMode.free;
        
        private Balder.View.Camera _gameCamera;

        public xna.Vector3 position;
        private xna.Vector3 desiredPosition;
        public xna.Vector3 target;
        private xna.Vector3 desiredTarget;
        private xna.Vector3 offsetDistance;

        private float yaw, pitch, roll;
        private float speed;

        private xna.Matrix cameraRotation;
        public xna.Matrix viewMatrix, projectionMatrix;

        public Camera(Balder.View.Camera gameCamera)
        {
            _gameCamera = gameCamera;
            ResetCamera();
        }

        public void ResetCamera()
        {
            position = new xna.Vector3(0, 2, 50);
            desiredPosition = position;
            target = new xna.Vector3();
            desiredTarget = target;

            offsetDistance = new xna.Vector3(0, 2, 50);

            yaw = 0.0f;
            pitch = 0.0f;
            roll = 0.0f;

            speed = .3f;

            cameraRotation = xna.Matrix.Identity;
            viewMatrix = xna.Matrix.Identity;
            projectionMatrix = xna.Matrix.CreatePerspectiveFieldOfView(xna.MathHelper.ToRadians(45.0f), 16 / 9, .5f, 500f);
        }

        public void Update(xna.Matrix chasedObjectsWorld)
        {
            HandleInput();
            UpdateViewMatrix(chasedObjectsWorld);
        }

        private void HandleInput()
        {
            

            //Rotate Camera
            if (InputManager.IsKeyDown(Key.J))
            {
                if (currentCameraMode != CameraMode.chase)
                {
                    yaw += -.02f;
                }
                else
                {
                    //This is to make the panning more prominent in the Chase Camera.
                    yaw += -.2f;
                }
            }
            if (InputManager.IsKeyDown(Key.L))
            {
                if (currentCameraMode != CameraMode.chase)
                {
                    yaw += .02f;
                }
                else
                {
                    yaw += .2f;
                }
            }
            if (InputManager.IsKeyDown(Key.I))
            {
                if (currentCameraMode != CameraMode.chase)
                {
                    pitch += .02f;
                }
                else
                {
                    pitch += -.2f;
                }
            }
            if (InputManager.IsKeyDown(Key.K))
            {
                if (currentCameraMode != CameraMode.chase)
                {
                    pitch += -.02f;
                }
                else
                {
                    pitch += .2f;
                }
            }
            if (InputManager.IsKeyDown(Key.U))
            {
                roll += .02f;
            }
            if (InputManager.IsKeyDown(Key.O))
            {
                roll += -.02f;
            }

            //Move Camera
            if (currentCameraMode == CameraMode.free)
            {
                if (InputManager.IsKeyDown(Key.W))
                {
                    MoveCamera(cameraRotation.Forward);
                }
                if (InputManager.IsKeyDown(Key.S))
                {
                    MoveCamera(-cameraRotation.Forward);
                }
                if (InputManager.IsKeyDown(Key.A))
                {
                    MoveCamera(cameraRotation.Right);
                }
                if (InputManager.IsKeyDown(Key.D))
                {
                    MoveCamera(-cameraRotation.Right);
                }
                if (InputManager.IsKeyDown(Key.E))
                {
                    MoveCamera(cameraRotation.Up);
                }
                if (InputManager.IsKeyDown(Key.Q))
                {
                    MoveCamera(-cameraRotation.Up);
                }
            }
        }

        private void MoveCamera(xna.Vector3 addedVector)
        {
            position += speed * addedVector;
        }

        private void UpdateViewMatrix(xna.Matrix chasedObjectsWorld)
        {
            switch (currentCameraMode)
            {
                case CameraMode.free:

                    cameraRotation.Forward.Normalize();
                    cameraRotation.Up.Normalize();
                    cameraRotation.Right.Normalize();

                    cameraRotation *= xna.Matrix.CreateFromAxisAngle(cameraRotation.Right, pitch);
                    cameraRotation *= xna.Matrix.CreateFromAxisAngle(cameraRotation.Up, yaw);
                    cameraRotation *= xna.Matrix.CreateFromAxisAngle(cameraRotation.Forward, roll);

                    yaw = 0.0f;
                    pitch = 0.0f;
                    roll = 0.0f;

                    target = position + cameraRotation.Forward;

                    break;

                case CameraMode.chase:

                    cameraRotation.Forward.Normalize();
                    chasedObjectsWorld.Right.Normalize();
                    chasedObjectsWorld.Up.Normalize();

                    cameraRotation = xna.Matrix.CreateFromAxisAngle(cameraRotation.Forward, roll);

                    desiredTarget = chasedObjectsWorld.Translation;
                    target = desiredTarget;

                    target += chasedObjectsWorld.Right * yaw;
                    target += chasedObjectsWorld.Up * pitch;

                    desiredPosition = xna.Vector3.Transform(offsetDistance, chasedObjectsWorld);
                    position = xna.Vector3.SmoothStep(position, desiredPosition, .15f);

                    yaw = xna.MathHelper.SmoothStep(yaw, 0f, .1f);
                    pitch = xna.MathHelper.SmoothStep(pitch, 0f, .1f);
                    roll = xna.MathHelper.SmoothStep(roll, 0f, .2f);

                    break;

                case CameraMode.orbit:

                    cameraRotation.Forward.Normalize();

                    cameraRotation = xna.Matrix.CreateRotationX(pitch) * xna.Matrix.CreateRotationY(yaw) * xna.Matrix.CreateFromAxisAngle(cameraRotation.Forward, roll);

                    desiredPosition = xna.Vector3.Transform(offsetDistance, cameraRotation);
                    desiredPosition += chasedObjectsWorld.Translation;
                    position = desiredPosition;

                    target = chasedObjectsWorld.Translation;

                    roll = xna.MathHelper.SmoothStep(roll, 0f, .2f);

                    break;
            }

            //We'll always use this line of code to set up the View Matrix.
            viewMatrix = xna.Matrix.CreateLookAt(position, target, cameraRotation.Up);

            _gameCamera.Position = Utils.VectorXnaToBalder(position);
            _gameCamera.Target = Utils.VectorXnaToBalder(target);

        }

        //This cycles through the different camera modes.
        public void SwitchCameraMode()
        {
            ResetCamera();

            currentCameraMode++;

            if ((int)currentCameraMode > 2)
            {
                currentCameraMode = 0;
            }
        }
    }
}
